﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace com.susi.util.toolkit
{
    public class SignPropertySource : IPropertySource, IEnumerable<string>
    {
        Dictionary<string, float> mFloDic = new Dictionary<string, float>();
        Dictionary<string, Func<float>> mFloGtr = new Dictionary<string, Func<float>>();
        Dictionary<string, string> mStrDic = new Dictionary<string, string>();
        Dictionary<string, Func<string>> mStrGtr = new Dictionary<string, Func<string>>();

        public IEnumerable<string> Keys { get; }

        public void Set(string key, float value)
        {
            Set(key);
            mFloDic.Add(key, value);
        }
        public void Set(string key, Func<float> getter)
        {
            Set(key);
            mFloGtr.Add(key, getter);
        }
        public void Set(string key, string value)
        {
            Set(key);
            mStrDic.Add(key, value);
        }
        public void Set(string key, Func<string> getter)
        {
            Set(key);
            mStrGtr.Add(key, getter);
        }
        public void Set(string key)
        {
            if (mFloDic.Remove(key)) return;
            else if (mFloGtr.Remove(key)) return;
            else if (mStrDic.Remove(key)) return;
            else if (mStrGtr.Remove(key)) return;
        }

        public bool TryGet(string key, out float value)
        {
            if (mFloDic.TryGetValue(key, out value)) return true;
            else if (mFloGtr.TryGetValue(key, out var getter))
            {
                value = getter();
                return true;
            }
            return false;
        }
        public bool TryGet(string key, out string value)
        {
            if (mStrDic.TryGetValue(key, out value)) return true;
            else if (mStrGtr.TryGetValue(key, out var getter))
            {
                value = getter();
                return true;
            }
            return false;
        }

        public bool ContainsFloKey(string key)
        {
            return mFloDic.ContainsKey(key) || mFloGtr.ContainsKey(key);
        }
        public bool ContainsStrKey(string key)
        {
            return mStrDic.ContainsKey(key) || mStrGtr.ContainsKey(key);
        }
        public bool ContainsKey(string key) => ContainsFloKey(key) || ContainsStrKey(key);

        IEnumerator<string> IEnumerable<string>.GetEnumerator()
        {
            return new KeyEnumerator(this);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new KeyEnumerator(this);
        }

        public float GetFlo(string key)
        {
            if (mFloDic.TryGetValue(key, out var flo)) return flo;
            if (mFloGtr.TryGetValue(key, out var func)) return func();
            return default;
        }

        public string GetStr(string key)
        {
            if (mStrDic.TryGetValue(key, out var flo)) return flo;
            if (mStrGtr.TryGetValue(key, out var func)) return func();
            return default;
        }

        private class KeyEnumerator : IEnumerator<string>
        {
            IEnumerator<IEnumerator<string>> enums;
            public KeyEnumerator(SignPropertySource propertySource)
            {
                var item = new IEnumerator<string>[] {
                    propertySource.mFloDic.Keys.GetEnumerator(),
                    propertySource.mFloGtr.Keys.GetEnumerator(),
                    propertySource.mStrDic.Keys.GetEnumerator(),
                    propertySource.mStrGtr.Keys.GetEnumerator(),
                    };
                enums = item.AsEnumerable().GetEnumerator();
            }

            public string Current => enums.Current.Current;

            object IEnumerator.Current => Current;

            public void Dispose()
            {
                enums.Reset();
                while (enums.MoveNext()) enums.Current.Dispose();
                enums.Dispose();
            }

            public bool MoveNext()
            {
                if (enums.Current.MoveNext()) return true;
                else if (enums.MoveNext() && enums.Current.MoveNext()) return true;
                return false;
            }

            public void Reset()
            {
                enums.Reset();
                while (enums.MoveNext()) enums.Current.Reset();
                enums.Reset();
            }
        }
    }
}
